package com.ddxz.tool.core.convert;

import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.ddxz.tool.core.exception.DdxzBaseException;
import lombok.extern.slf4j.Slf4j;

import java.nio.charset.Charset;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

import static com.ddxz.tool.core.crypto.codec.CryptoConstData.HEX_ONE;
import static com.ddxz.tool.core.exception.DdxzErrorStatus.CONVERT_ERROR;

/**
 * @author 朱宗刚
 * @version 1.0.0
 * @description com.casic.exchange.com.casic.common.tools.convert.StringUtil
 * @date 2020/2/28
 */

@Slf4j
public class StringUtil {
    private static final String DEFAULT_DELIMITER = ",";

    public static int getIndexOf(CharSequence src, CharSequence sub, int startIndex, boolean ignore) {
        if(ignore) {
            return src.toString().toLowerCase().indexOf(sub.toString().toLowerCase(), startIndex);
        } else {
            return src.toString().indexOf(sub.toString(), startIndex);
        }
    }

    public static int getLastIndexOf(CharSequence src, CharSequence sub, int startIndex, boolean ignore) {
        if(ignore) {
            return src.toString().toLowerCase().lastIndexOf(sub.toString().toLowerCase(), startIndex);
        } else {
            return src.toString().lastIndexOf(sub.toString(), startIndex);
        }
    }

    public static int getIndexOf(CharSequence src, CharSequence sub, int startIndex) {
        return getIndexOf(src, sub, startIndex, false);
    }

    public static int getLastIndexOf(CharSequence src, CharSequence sub, int startIndex) {
        return getLastIndexOf(src, sub, startIndex, false);
    }

    public static int getIndexOfIgnore(CharSequence src, CharSequence sub, int startIndex) {
        return getIndexOf(src, sub, startIndex, true);
    }

    public static int getLastIndexOfIgnore(CharSequence src, CharSequence sub, int startIndex) {
        return getLastIndexOf(src, sub, startIndex, true);
    }

    public static int getIndexOf(CharSequence src, CharSequence sub) {
        return getIndexOf(src, sub, 0);
    }

    public static int getLastIndexOf(CharSequence src, CharSequence sub) {
        return getLastIndexOf(src, sub, 0);
    }

    public static int getIndexOfIgnore(CharSequence src, CharSequence sub) {
        return getIndexOf(src, sub, 0);
    }

    public static int getLastIndexOfIgnore(CharSequence src, CharSequence sub) {
        return getLastIndexOf(src, sub, 0);
    }

    public static boolean isHex(String src) {
        if(isEmpty(src)) {
            return false;
        }

        return HEX.matcher(src).matches();
    }

    public static byte[] castToBytes(String src) {
        if(!isHex(src)) {
            return null;
        }

        char[] hexData = src.toCharArray();
        int len = hexData.length;

        if ((len & HEX_ONE) != 0) {
            throw new RuntimeException("Odd number of characters.");
        }

        byte[] out = new byte[len >> 1];

        // two characters form the hex value.
        for (int i = 0, j = 0; j < len; i++) {
            int f = toDigit(hexData[j], j) << 4;
            j++;
            f = f | toDigit(hexData[j], j);
            j++;
            out[i] = (byte) (f & 0xFF);
        }

        return out;
    }

    private static int toDigit(char ch, int index) {
        int digit = Character.digit(ch, 16);
        if (digit == -1) {
            throw new RuntimeException("Illegal hexadecimal character " + ch + " at index " + index);
        }
        return digit;
    }

    public static boolean isEmpty(String str) {
        return null == str || str.isEmpty();
    }

    public static boolean isBlank(String str) {
        return null == str || str.trim().isEmpty();
    }

    public static <K, V> String join(Map<K, V> src, String delimiter, String keyValueDelimiter, boolean ignoreNull, Object... others) {
        StringBuilder builder = new StringBuilder();
        src.forEach((key, val) -> {
            if(null != val) {
                builder.append(delimiter).append(key).append(keyValueDelimiter).append(val);
            } else if(!ignoreNull) {
                builder.append(delimiter).append(key).append(keyValueDelimiter).append("null");
            }
        });

        for(Object obj : others) {
            builder.append(delimiter).append(obj);
        }

        return builder.toString().substring(delimiter.length());
    }

    public static <T> String join(T[] objs) {
        return join(objs, DEFAULT_DELIMITER);
    }

    public static <T> String join(T[] objs, String delimiter) {
        if(CollectionUtil.isEmpty(objs)) {
            return "";
        }

        StringBuilder builder = new StringBuilder();
        for(T obj : objs) {
            builder.append(obj).append(delimiter);
        }

        return builder.substring(0, builder.length() - delimiter.length());
    }

    public static String join(boolean[] objs) {
        return join(objs, DEFAULT_DELIMITER);
    }

    public static String join(boolean[] objs, String delimiter) {
        if(CollectionUtil.isEmpty(objs)) {
            return "";
        }

        StringBuilder builder = new StringBuilder();
        for(boolean obj : objs) {
            builder.append(obj).append(delimiter);
        }

        return builder.substring(0, builder.length() - delimiter.length());
    }

    public static String join(char[] objs) {
        return join(objs, DEFAULT_DELIMITER);
    }

    public static String join(char[] objs, String delimiter) {
        if(CollectionUtil.isEmpty(objs)) {
            return "";
        }

        StringBuilder builder = new StringBuilder();
        for(char obj : objs) {
            builder.append(obj).append(delimiter);
        }

        return builder.substring(0, builder.length() - delimiter.length());
    }

    public static String fromBytes(byte[] bytes) {
        return fromBytes(bytes, UTF8);
    }

    public static String fromBytes(byte[] bytes, Charset charset) {
        return new String(bytes, charset);
    }

    public static String fromBytes(byte[] bytes, String charset) {
        return fromBytes(bytes, Charset.forName(charset));
    }

    public static String join(byte[] objs) {
        return join(objs, DEFAULT_DELIMITER);
    }

    public static Object getValue(String src, Class<? extends Object> clz) {
        if(isEmpty(src)) {
            return null;
        }

        if(String.class.isAssignableFrom(clz)) {
            return src;
        }

        if(Long.class.isAssignableFrom(clz)) {
            return toLong(src);
        }

        if(Integer.class.isAssignableFrom(clz)) {
            return toInteger(src);
        }

        if(Short.class.isAssignableFrom(clz)) {
            return toInteger(src).shortValue();
        }

        if(Double.class.isAssignableFrom(clz)) {
            return toDouble(src);
        }

        if(Float.class.isAssignableFrom(clz)) {
            return toFloat(src);
        }

        if(JSONObject.class.isAssignableFrom(clz)) {
            return JSONObject.parseObject(src);
        }

        if(JSONArray.class.isAssignableFrom(clz)) {
            return JSONArray.parseArray(src);
        }

        throw new DdxzBaseException(CONVERT_ERROR, clz.getName());
    }

    public static String join(byte[] objs, String delimiter) {
        if(CollectionUtil.isEmpty(objs)) {
            return "";
        }

        StringBuilder builder = new StringBuilder();
        for(byte obj : objs) {
            builder.append(obj).append(delimiter);
        }

        return builder.substring(0, builder.length() - delimiter.length());
    }

    public static String join(int[] objs) {
        return join(objs, DEFAULT_DELIMITER);
    }

    public static String join(int[] objs, String delimiter) {
        if(CollectionUtil.isEmpty(objs)) {
            return "";
        }

        StringBuilder builder = new StringBuilder();
        for(int obj : objs) {
            builder.append(obj).append(delimiter);
        }

        return builder.substring(0, builder.length() - delimiter.length());
    }

    public static String join(short[] objs) {
        return join(objs, DEFAULT_DELIMITER);
    }

    public static String join(short[] objs, String delimiter) {
        if(CollectionUtil.isEmpty(objs)) {
            return "";
        }

        StringBuilder builder = new StringBuilder();
        for(short obj : objs) {
            builder.append(obj).append(delimiter);
        }

        return builder.substring(0, builder.length() - delimiter.length());
    }

    public static String join(float[] objs) {
        return join(objs, DEFAULT_DELIMITER);
    }

    public static String join(float[] objs, String delimiter) {
        if(CollectionUtil.isEmpty(objs)) {
            return "";
        }

        StringBuilder builder = new StringBuilder();
        for(float obj : objs) {
            builder.append(obj).append(delimiter);
        }

        return builder.substring(0, builder.length() - delimiter.length());
    }

    public static String join(long[] objs) {
        return join(objs, DEFAULT_DELIMITER);
    }

    public static String join(long[] objs, String delimiter) {
        if(CollectionUtil.isEmpty(objs)) {
            return "";
        }

        StringBuilder builder = new StringBuilder();
        for(long obj : objs) {
            builder.append(obj).append(delimiter);
        }

        return builder.substring(0, builder.length() - delimiter.length());
    }

    public static String join(double[] objs) {
        return join(objs, DEFAULT_DELIMITER);
    }

    public static String join(double[] objs, String delimiter) {
        if(CollectionUtil.isEmpty(objs)) {
            return "";
        }

        StringBuilder builder = new StringBuilder();
        for(double obj : objs) {
            builder.append(obj).append(delimiter);
        }

        return builder.substring(0, builder.length() - delimiter.length());
    }

    public static String hexString(Byte[] context) {
        if(CollectionUtil.isEmpty(context)) {
            return "";
        }

        StringBuilder builder = new StringBuilder();

        for(Byte b : context) {
            builder.append(String.format("%02X", b));
        }
        return builder.toString();
    }

    public static byte[] getBytes(String src) {
        return getBytes(src, UTF8);
    }

    public static byte[] getBytes(String src, Charset charset) {
        return src.getBytes(charset);
    }

    public static byte[] getBytes(String src, String charset) {
        return src.getBytes(Charset.forName(charset));
    }

    public static String hexString(byte[] context) {
        if(CollectionUtil.isEmpty(context)) {
            return "";
        }

        StringBuilder builder = new StringBuilder();

        for(byte b : context) {
            builder.append(String.format("%02X", b));
        }
        return builder.toString();
    }

    public static String getMessage(String fmt, Object... args) {
        StringBuilder builder = new StringBuilder(fmt);
        int pos;

        for (Object arg : args) {
            pos = builder.indexOf("{}");
            if(pos == -1) {
                break;
            }

            builder.replace(pos, pos + 2, arg.toString());
        }

        return builder.toString();
    }

    public static <T> String join(Collection<T> colls) {
        return join(colls, DEFAULT_DELIMITER);
    }

    public static <T> String join(Collection<T> colls, String delimiter) {
        if(CollectionUtil.isEmpty(colls)) {
            return "";
        }

        StringBuilder builder = new StringBuilder();
        colls.forEach(item -> builder.append(item.toString()).append(delimiter));

        return builder.substring(0, builder.length() - delimiter.length());
    }

    public static List<String> getList(String src) {
        return getList(src, DEFAULT_DELIMITER);
    }

    public static List<String> getList(String str, String delimiter) {
        if(isEmpty(str)) {
            return new ArrayList<>();
        }

        return Arrays.asList(str.split(delimiter));
    }

    public static Integer toInteger(String str) {
        if(TEN.matcher(str).find()) {
            return Optional.of(Integer.parseInt(str)).orElse(0);
        } else {
            return 0;
        }
    }

    public static Long toLong(String str) {
        if(TEN.matcher(str).find()) {
            return Optional.of(Long.parseLong(str)).orElse(0L);
        } else {
            return 0L;
        }
    }

    public static Float toFloat(String str) {
        return Optional.of(Float.parseFloat(str)).orElse(0.0F);
    }

    public static Double toDouble(String str) {
        return Optional.of(Double.parseDouble(str)).orElse(0.0);
    }

    public static String trim(String value, char ch) {
        String tmp = value.trim();
        int len = tmp.length();
        int st = 0;
        char[] val = tmp.toCharArray();

        while ((st < len) && (val[st] == ch)) {
            st++;
        }
        while ((st < len) && (val[len - 1] == ch)) {
            len--;
        }

        return ((st > 0) || (len < tmp.length())) ? tmp.substring(st, len) : tmp;
    }

    public static String ltrim(String value, char ch) {
        String tmp = value.trim();
        int st = 0;

        while ((st < value.length()) && (value.charAt(st) == ch)) {
            st++;
        }

        return st > 0 ? tmp.substring(st) : tmp;
    }

    public static String rtrim(String value, char ch) {
        String tmp = value.trim();
        int st = value.length();

        while ((st >= 0) && (value.charAt(st - 1) == ch)) {
            --st;
        }

        return st == value.length() ? tmp : tmp.substring(0, st);
    }

    public static String lineToHump(String src) {
        StringBuffer builder = new StringBuffer();
        src = src.toLowerCase();
        Matcher matcher = PATTERN.matcher(src);
        while(matcher.find()) {
            matcher.appendReplacement(builder, matcher.group(1).toUpperCase());
        }

        matcher.appendTail(builder);

        return builder.toString();
    }

    public static String humpToLine(String src) {
        return src.replaceAll("[A-Z]", "_$0").toLowerCase();
    }

    private static final Pattern PATTERN = Pattern.compile("_(\\w)");

    public static final Charset UTF8 = Charset.forName("UTF-8");

    /** 16进制字符串 */
    private static final Pattern HEX = Pattern.compile("^[a-f0-9]+$", Pattern.CASE_INSENSITIVE);

    private static final Pattern TEN = Pattern.compile("^[+-]?[0-9]+$");

    public static Short toShort(String src) {
        return Optional.of(Short.parseShort(src)).orElse((short) 0);
    }
}
